package com.cybersapien.comng.engine.action;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

import com.cybersapien.comng.engine.action.IAction.ActionParams;
import com.cybersapien.comng.engine.action.IAction.Result;
import com.cybersapien.comng.engine.action.annotation.ActionFireEvent;
import com.cybersapien.comng.engine.action.exception.ActionRefused;
import com.cybersapien.comng.engine.event.engine.IEventEngine;
import com.cybersapien.comng.engine.exception.TechnicalException;
import com.cybersapien.comng.engine.type.IStack;
import com.cybersapien.comng.engine.type.RawStack;

public class ActionManager {

	/* ====== SINGLETON MANAGEMENT ===== */

	static private ActionManager instance = null;

	static public ActionManager getInstance() {
		if (instance == null) {
			initInstance();
		}
		return instance;
	}

	static private synchronized void initInstance() {
		if (instance == null) {
			instance = new ActionManager();
		}
	}

	/* ===== */

	public void executeAction(IAction action, ActionParams params) throws ActionRefused {
		this.validateAction(action, params);
		Result result = action.testAction(params);
		IStack<IEventEngine> events = getEventsForResult(action, result, params);
	}

	protected void validateAction(IAction action, ActionParams params) throws ActionRefused {
		if (!action.validateAction(params)) {
			ActionRefused e = new ActionRefused(action, params);
			throw e;
		}
	}

	protected IStack<IEventEngine> getEventsForResult(IAction action, Result result, ActionParams params) {

		ActionFireEvent annotation = action.getClass().getAnnotation(ActionFireEvent.class);

		Class<? extends IEventEngine>[] listEventClasses = null;
		switch (result) {
		case EPIC_SUCCESS:
			listEventClasses = annotation.epicSuccessEvents();
			break;
		case SUCCESS:
			listEventClasses = annotation.successEvents();
			break;
		case EPIC_FAILURE:
			listEventClasses = annotation.epicFailureEvents();
			break;
		case FAILURE:
			listEventClasses = annotation.failureEvents();
			break;
		}

		IStack<IEventEngine> events = new RawStack<IEventEngine>();
		for (Class<? extends IEventEngine> eventClass : listEventClasses) {
			IEventEngine event = createEvent(eventClass, action, params);
			events.push(event);
		}

		return events;
	}

	protected IEventEngine createEvent(Class<? extends IEventEngine> eventClass, IAction action, ActionParams params) {

		IEventEngine event = null;
		Constructor<? extends IEventEngine> constructor = null;
		try {
			constructor = eventClass.getConstructor(IAction.class, ActionParams.class);
		} catch (SecurityException e) {
			TechnicalException.throwMe("Event " + eventClass + " has no valid constructor.", e);
		} catch (NoSuchMethodException e) {
			TechnicalException.throwMe("Event " + eventClass + " has no valid constructor.", e);
		}

		try {
			event = constructor.newInstance(action, params);
		} catch (IllegalArgumentException e) {
			TechnicalException.throwMe("Event " + eventClass + " instantiation failed.", e);
		} catch (InstantiationException e) {
			TechnicalException.throwMe("Event " + eventClass + " instantiation failed.", e);
		} catch (IllegalAccessException e) {
			TechnicalException.throwMe("Event " + eventClass + " instantiation failed.", e);
		} catch (InvocationTargetException e) {
			TechnicalException.throwMe("Event " + eventClass + " instantiation failed.", e);
		}

		return event;
	}
}
